﻿//==============================================================================  
//Copyright (C) 2012-2015 9UN.ORG. All rights reserved. 
//GUID：46172595-6b69-4526-b9da-426d663f5b05
//CLR Version: 4.0.30319.18033
//Code Author：Kevin Wang
//Contact：Email(Admin@9un.org),QQ(265382 or 74344)
//Filename：DoubleWindowBase
//Namespace：RuanRang.Media.Core
//Functions：DoubleWindowBase  
//Created by Kevin Wang at 2013/2/2 23:05:09 http://blog.9un.org
//============================================================================== 
#region

using System;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using RuanRang.Media.Core;
using RuanRang.Media.Core.Common;
using RuanRang.Media.Core.Internal;
using RuanRang.Media.Core.Io;

#endregion

namespace RuanRang.Media.WinForms
{
    /// <summary>
    ///     User control provides straightforward access to libvlcnet features.
    /// </summary>
    public partial class VlcPlayerControl : UserControl
    {
        #region Events
        /// <summary>
        /// Occurs when [media player media changed].
        /// </summary>
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, IntPtr> MediaPlayerMediaChanged
        {
            add { ((BaseMediaPlayer)Player).MediaPlayerMediaChanged += value; }
            remove { ((BaseMediaPlayer)Player).MediaPlayerMediaChanged -= value; }
        }

        /// <summary>
        /// Occurs when [media player buffering].
        /// </summary>
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, float> MediaPlayerBuffering
        {
            add { ((BaseMediaPlayer)Player).MediaPlayerBuffering += value; }
            remove { ((BaseMediaPlayer)Player).MediaPlayerBuffering -= value; }
        }

        /// <summary>
        /// Occurs when [media player time changed].
        /// </summary>
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, long> MediaPlayerTimeChanged
        {
            add { ((BaseMediaPlayer)Player).MediaPlayerTimeChanged += value; }
            remove { ((BaseMediaPlayer)Player).MediaPlayerTimeChanged -= value; }
        }

        /// <summary>
        /// Occurs when [media player position changed].
        /// </summary>
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, float> MediaPlayerPositionChanged
        {
            add { ((BaseMediaPlayer)Player).MediaPlayerPositionChanged += value; }
            remove { ((BaseMediaPlayer)Player).MediaPlayerPositionChanged -= value; }
        }

        ///// <summary>
        ///// Occurs when [media player seekable changed].
        ///// </summary>
        //[Category("RuanRang.com VideoLan Event")]
        //public event VlcEventHandler<BaseMediaPlayer, int> MediaPlayerSeekableChanged
        //{
        //    add { ((BaseMediaPlayer)Player).MediaPlayerSeekableChanged += value; }
        //    remove { ((BaseMediaPlayer)Player).MediaPlayerSeekableChanged -= value; }
        //}

        ///// <summary>
        ///// Occurs when [media player pausable changed].
        ///// </summary>
        //[Category("RuanRang.com VideoLan Event")]
        //public event VlcEventHandler<BaseMediaPlayer, int> MediaPlayerPausableChanged
        //{
        //    add { ((BaseMediaPlayer)Player).MediaPlayerPausableChanged += value; }
        //    remove { ((BaseMediaPlayer)Player).MediaPlayerPausableChanged -= value; }
        //}

        /// <summary>
        /// Occurs when [media player title changed].
        /// </summary>
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, int> MediaPlayerTitleChanged
        {
            add { ((BaseMediaPlayer)Player).MediaPlayerTitleChanged += value; }
            remove { ((BaseMediaPlayer)Player).MediaPlayerTitleChanged -= value; }
        }

        /// <summary>
        /// Occurs when [media player snapshot taken].
        /// </summary>
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, IntPtr> MediaPlayerSnapshotTaken
        {
            add { ((BaseMediaPlayer)Player).MediaPlayerSnapshotTaken += value; }
            remove { ((BaseMediaPlayer)Player).MediaPlayerSnapshotTaken -= value; }
        }

        /// <summary>
        /// Occurs when [media player length changed].
        /// </summary>
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, long> MediaPlayerLengthChanged
        {
            add { ((BaseMediaPlayer)Player).MediaPlayerLengthChanged += value; }
            remove { ((BaseMediaPlayer)Player).MediaPlayerLengthChanged -= value; }
        }

        /// <summary>
        /// Occurs when [media player vout].
        /// </summary>
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, int> MediaPlayerVout
        {
            add { ((BaseMediaPlayer)Player).MediaPlayerVout += value; }
            remove { ((BaseMediaPlayer)Player).MediaPlayerVout -= value; }
        }

        //
        /// <summary>
        /// Occurs when [media player state changed]. 非LIBVLC内部事件
        /// </summary>
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, VlcPlayerState> MediaPlayerStateChanged
        {
            add { ((BaseMediaPlayer)Player).MediaPlayerStateChanged += value; }
            remove { ((BaseMediaPlayer)Player).MediaPlayerStateChanged -= value; }
        }
        #endregion

        /// <summary>
        /// The _media library factory
        /// </summary>
        private ILibVlcFactory _mediaLibraryFactory;

        /// <summary>
        /// The VLC window control
        /// </summary>
        private VlcWindowControl _vlcWindowControl;

        /// <summary>
        /// Generates the parameters.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <returns>System.String[][].</returns>
        public string[] GenerateParameters(string path)
        {
            return new[]
                       {
                           "--reset-config",
                           "--no-snapshot-preview",
                           "--ignore-config",
                           "--no-video-title-show",
                           "--intf", "rc",
                           "--no-osd",
                           //"--sharpen-sigma=2.0",
                           "--plugin-path", path
                       };
        }

        /// <summary>
        ///     Default constructor.
        /// </summary>
        public VlcPlayerControl()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Is VCL subsystem initialized.
        /// </summary>
        /// <value><c>true</c> if this instance is initialized; otherwise, <c>false</c>.</value>
        public bool IsInitialized { get; private set; }

        /// <summary>
        /// Returns player backed by control.
        /// </summary>
        /// <value>The player.</value>
        public VlcMediaPlayer Player { get; private set; }

        /// <summary>
        /// Current playing item.
        /// </summary>
        /// <value>The current playing.</value>
        public MediaInput CurrentPlaying { get; private set; }

        /// <summary>
        ///     Position of playing movie.
        ///     (0.0 - 1.0).
        /// </summary>
        public double Position
        {
            get
            {
                return Player == null ? 0f : Player.Position;
            }
            set
            {
                if (Player == null)
                {
                    return;
                }
                Player.Position = (float)value;
            }
        }

        /// <summary>
        ///     Current playing time.
        /// </summary>
        public TimeSpan Time
        {
            get
            {
                return Player == null ? new TimeSpan(0) : Player.Time;
            }
            set
            {
                if (Player == null)
                {
                    return;
                }
                Player.Time = value;
            }
        }

        /// <summary>
        ///     Volume level.
        /// </summary>
        public int Volume
        {
            get
            {
                return Player == null ? -1 : Player.Volume;
            }
            set
            {
                if (Player == null)
                {
                    return;
                }
                Player.Volume = value;
            }
        }


        /// <summary>
        /// Control state.
        /// </summary>
        /// <value>
        /// The state.
        /// </value>
        public VlcPlayerState State
        {
            get
            {
                return Player != null ? Player.State : VlcPlayerState.Idle;
            }
        }

        /// <summary>
        /// Lazies the initialize.
        /// </summary>
        private void LazyInitialize()
        {
            if (!IsInitialized)
            {
                Initialize();
            }
        }

        /// <summary>
        /// GETs the startup path.
        /// </summary>
        /// <returns>System.String.</returns>
        private static string GetStartupPath()
        {
            return Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
        }

        /// <summary>
        /// Initializes VLC resources will be used by control. Default VLC
        /// parameters is used in this overload.
        /// </summary>
        /// <exception cref="InvalidOperationException">Reinitialization is not allowed.</exception>
        /// <exception cref="MediaPlayerException">Media player cannot be initialized.</exception>
        public void Initialize()
        {
            var path = Path.Combine(GetStartupPath(), "plugins");
            Initialize(null, path);
        }

        /// <summary>
        /// Initializes the specified sync events to.
        /// </summary>
        /// <param name="syncEventsTo">The sync events to.</param>
        public void Initialize(ISynchronizeInvoke syncEventsTo)
        {
            var path = Path.Combine(GetStartupPath(), "plugins");
            Initialize(syncEventsTo, GenerateParameters(path), null);
        }

        /// <summary>
        /// Initializes VLC resources will be used by control. Default VLC
        /// parameters is used in this overload.
        /// </summary>
        /// <param name="syncEventsTo">The sync events to.</param>
        /// <param name="vlcPath">The VLC path.</param>
        /// <exception cref="InvalidOperationException">Reinitialization is not allowed.</exception>
        /// <exception cref="MediaPlayerException">Media player cannot be initialized.</exception>
        public void Initialize(ISynchronizeInvoke syncEventsTo, string vlcPath)
        {
            var path = Path.Combine(vlcPath, "plugins");
            Initialize(syncEventsTo, GenerateParameters(path), null);
        }

        /// <summary>
        /// Initializes VLC resources will be used by control.
        /// </summary>
        /// <param name="syncEventsTo">The sync events to.</param>
        /// <param name="vlcParameters">Parameters passed to VLC library</param>
        /// <exception cref="InvalidOperationException">Reinitialization is not allowed.</exception>
        /// <exception cref="MediaPlayerException">Media player cannot be initialized.</exception>
        public void Initialize(ISynchronizeInvoke syncEventsTo, string[] vlcParameters)
        {
            Initialize(syncEventsTo, vlcParameters, null);
        }

        /// <summary>
        /// Initializes VLC resources will be used by control.
        /// </summary>
        /// <param name="syncEventsTo">The sync events to.</param>
        /// <param name="vlcParameters">Parameters passed to VLC library</param>
        /// <param name="handler">Callback which can be used to initialize factory instance parameters.</param>
        /// <exception cref="System.InvalidOperationException">This object does not support multi time initialization.</exception>
        /// <exception cref="InvalidOperationException">Reinitialization is not allowed.</exception>
        /// <exception cref="MediaPlayerException">Media player cannot be initialized.</exception>
        public void Initialize(ISynchronizeInvoke syncEventsTo, string[] vlcParameters,
                               VlcMediaLibraryFactoryInitHandler handler)
        {
            if (IsInitialized)
            {
                throw new InvalidOperationException("This object does not support multi time initialization.");
            }
            //
            _mediaLibraryFactory = InitializeFactory(vlcParameters);
            if (_mediaLibraryFactory == null)
            {
                throw new InvalidOperationException("Initialize LibVlcFactory failed.");
            }
            IsInitialized = true;
            if (handler != null)
            {
                handler(_mediaLibraryFactory as LibVlcFactory);
            }
            //
            Player = (VlcMediaPlayer)_mediaLibraryFactory.CreatePlayer(new PlayerOutput(_vlcWindowControl.Window), MediaPlayerType.MediaPlayer);
            if (Player == null)
            {
                throw new Exception("Create Player failed.");
            }
        }

        /// <summary>
        /// Initializes the factory.
        /// </summary>
        /// <param name="vlcParameters">The VLC parameters.</param>
        /// <returns>LibVlcFactory.</returns>
        protected ILibVlcFactory InitializeFactory(string[] vlcParameters)
        {
            if (vlcParameters == null)
            {
                vlcParameters = new string[]
                                    {
                                    };
            }
            return new LibVlcFactory(vlcParameters);
        }

        /// <summary>
        ///     Starts playing.
        /// </summary>
        /// <param name="mediaInput">Media to play.</param>
        public void Play(MediaInput mediaInput)
        {
            if (mediaInput == null)
            {
                throw new ArgumentNullException("mediaInput");
            }
            //
            LazyInitialize();
            CurrentPlaying = mediaInput;
            Player.SetMediaInput(mediaInput);
            Player.Volume = 100;
            Player.Play();
        }

        /// <summary>
        /// Pauses or resumes playing current movie.
        /// If no movie is loaded, no actions will be given.
        /// </summary>
        public void PauseOrResume()
        {
            if ((State != VlcPlayerState.Playing) && (State != VlcPlayerState.Paused))
            {
                return;
            }
            LazyInitialize();
            //
            switch (State)
            {
                case VlcPlayerState.Playing:
                    Player.Pause();
                    break;
                case VlcPlayerState.Paused:
                    Player.Resume();
                    break;
            }
        }

        /// <summary>
        /// Stops currently playing movie if player is not empty.
        /// </summary>
        public void Stop()
        {
            if ((State != VlcPlayerState.Playing) && (State != VlcPlayerState.Paused))
            {
                return;
            }
            //注意当正在播放视频的时候，如果停止播放，此时不能调试断点，因为会导致状态回调发生死锁现象，即程序会出现无响应的现象
            LazyInitialize();
            Player.Stop();
        }

        /// <summary>
        /// Uninitializes this instance.
        /// </summary>
        public void Uninitialize()
        {
            // Clean up vlclib resources
            if (Player != null)
            {
                Player.Dispose();
            }
        }

        /// <summary>
        /// </summary>
        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && components != null)
            {
                components.Dispose();
            }
            if (disposing)
            {
                Uninitialize();
            }
            base.Dispose(disposing);
        }
    }
}